package org.rscdaemon.server.model;

public class PathHandler {
	/**
	 * The path we are walking
	 */
	private Path path;
	/**
	 * The waypoint in the path we are currently at
	 */
	private int curWaypoint;
	/**
	 * The mob that this path belongs to
	 */
	private Mob mob;
	/**
	 * World instance
	 */
	private static final World world = World.getWorld();

	/**
	 * Constructs a new PathHandler belonging to the given Mob
	 */
	public PathHandler(Mob m) {
		mob = m;
		resetPath();
	}

	/**
	 * Creates a new path and sets us walking it
	 */
	public void setPath(int startX, int startY, byte[] waypointXoffsets, byte[] waypointYoffsets) {
		setPath(new Path(startX, startY, waypointXoffsets, waypointYoffsets));
	}
	
	/**
	 * Sets us on the given path
	 */
	public void setPath(Path path) {
		curWaypoint = -1;
		this.path = path;
	}
	
	/**
	 * Updates the point in the path to the next one
	 * assuming we are not finished
	 */
	public void updatePosition() {
		if (!finishedPath()) {
			setNextPosition();
		}
	}
	
	/**
	 * Resets the path (stops movement)
	 */
	protected void resetPath() {
		path = null;
		curWaypoint = -1;
	}

	/**
	 * Updates our position to the next in the path
	 */
	protected void setNextPosition() {
		int[] newCoords = {-1, -1};
		if (curWaypoint == -1) {
			if (atStart()) {
				curWaypoint = 0;
			}
			else {
				newCoords = getNextCoords(mob.getX(), path.getStartX(), mob.getY(), path.getStartY());
			}
		}
		if (curWaypoint > -1) {
			if (atWaypoint(curWaypoint)) {
				curWaypoint++;
			}
			if (curWaypoint < path.length()) {
				newCoords = getNextCoords(mob.getX(), path.getWaypointX(curWaypoint), mob.getY(), path.getWaypointY(curWaypoint));
			}
			else {
				resetPath();
			}
		}
		if (newCoords[0] > -1 && newCoords[1] > -1) {
			mob.setLocation(Point.location(newCoords[0], newCoords[1]));
		}
	}
	
	private boolean isBlocking(int x, int y, int bit) {
		TileValue t = world.getTileValue(x, y);
		return isBlocking(t.mapValue, (byte)bit) || isBlocking(t.objectValue, (byte)bit);
	}
	
	private boolean isBlocking(byte val, byte bit) {
		if((val & bit) != 0) { // There is a wall in the way
			return true;
		}
		if((val & 16) != 0) { // There is a diagonal wall here: \
			return true;
		}
		if((val & 32) != 0) { // There is a diagonal wall here: /
			return true;
		}
		if((val & 64) != 0) { // This tile is unwalkable
			return true;
		}
		return false;
	}

	/**
	 * Gets the next coordinate in the right direction
	 */
	protected int[] getNextCoords(int startX, int destX, int startY, int destY) {
		try {
			int[] coords = {startX, startY};
			boolean myXBlocked = false, myYBlocked = false, newXBlocked = false, newYBlocked = false;
			if (startX > destX) {
				myXBlocked = isBlocking(startX - 1, startY, 8); // Check right tiles left wall
				coords[0] = startX - 1;
			}
			else if(startX < destX) {
				myXBlocked = isBlocking(startX + 1, startY, 2); // Check left tiles right wall
				coords[0] = startX + 1;
			}
						
			if (startY > destY) {
				myYBlocked = isBlocking(startX, startY - 1, 4); // Check top tiles bottom wall
				coords[1] = startY - 1;
			}
			else if(startY < destY) {
				myYBlocked = isBlocking(startX, startY + 1, 1); // Check bottom tiles top wall
				coords[1] = startY + 1;
			}
			
			// If both directions are blocked OR we are going straight and the direction is blocked
			if((myXBlocked && myYBlocked) || (myXBlocked && startY == destY) || (myYBlocked && startX == destX)) {
				return cancelCoords();
			}

			if(coords[0] > startX) {
				newXBlocked = isBlocking(coords[0], coords[1], 2); // Check dest tiles right wall
			}
			else if(coords[0] < startX) {
				newXBlocked = isBlocking(coords[0], coords[1], 8); // Check dest tiles left wall
			}
			
			if(coords[1] > startY) {
				newYBlocked = isBlocking(coords[0], coords[1], 1); // Check dest tiles top wall
			}
			else if(coords[1] < startY) {
				newYBlocked = isBlocking(coords[0], coords[1], 4); // Check dest tiles bottom wall
			}
			
			// If both directions are blocked OR we are going straight and the direction is blocked
			if((newXBlocked && newYBlocked) || (newXBlocked && startY == coords[1]) || (myYBlocked && startX == coords[0])) {
				return cancelCoords();
			}
			
			// If only one direction is blocked, but it blocks both tiles
			if((myXBlocked && newXBlocked) || (myYBlocked && newYBlocked)) {
				return cancelCoords();
			}
			
			return coords;
		}
		catch(Exception e) {
			return cancelCoords();
		}
	}
	
	private int[] cancelCoords() {
		resetPath();
		return new int[]{-1, -1};
	}

	/**
	 * Checks if we have reached the end of our path
	 */
	public boolean finishedPath() {
		if(path == null) {
			return true;
		}
		if(path.length() > 0) {
			return atWaypoint(path.length() - 1);
		}
		else {
			return atStart();
		}
	}

	/**
	 * Checks if we are at the given waypoint
	 */
	protected boolean atWaypoint(int waypoint) {
		return path.getWaypointX(waypoint) == mob.getX() && path.getWaypointY(waypoint) == mob.getY();
	}

	/**
	 * Are we are the start of the path?
	 */
	protected boolean atStart() {
		return mob.getX() == path.getStartX() && mob.getY() == path.getStartY();
	}
	
	/**
	 * Attempts to create a path to the given coordinates
	 */
	public static Path makePath(int startX, int startY, int x1, int y1, int x2, int y2, boolean flag) {
		return null;
	}

}
